package com.atguigu.gulimall.order.service.impl;

import com.atguigu.gulimall.order.entity.OrderReturnReasonEntity;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Map;
import java.util.UUID;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.order.dao.OrderItemDao;
import com.atguigu.gulimall.order.entity.OrderItemEntity;
import com.atguigu.gulimall.order.service.OrderItemService;

@RabbitListener(queues = {"queue.c"})
@Service("orderItemService")
public class OrderItemServiceImpl extends ServiceImpl<OrderItemDao, OrderItemEntity> implements OrderItemService {

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderItemEntity> page = this.page(
                new Query<OrderItemEntity>().getPage(params),
                new QueryWrapper<OrderItemEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 注意，@RabbitListener要生效
     * 1 必须先要有@EnableRabbit注解
     * 2 所在的类的对象必须注入spring容器
     * 3 监听的队列必须存在
     * ===========================
     * 说明：
     * 1、如果只写一个参数，即用Message对象或者Object直接接，
     * 接到的是一个org.springframework.amqp.core.Message对象
     * 这里面包括头+体
     * 2、如果想获取发送时的对象，可以在第二个参数直接写上发送时的类型，如OrderReturnReasonEntity
     * 3、第三个参数可以获取通道对象
     * 4、一个queue可以有很多人同时监听，但默认每条消息只有一个人能收到
     * 可以用 --server.port改端口多启动一个客户端试试，这里注意，测试进程也会收到消息
     * 5、只有一个消息完全处理完（方法运行结束），才会接收下一个消息
     * <p>
     * 6、RabbitHandler和RabbitListener的区别
     *
     * @param message
     * @RabbitHandler 只能标在方法上
     * @RabbitListener 可以标在方法上，也可以标在类上。
     * 对了，也可以直接只写转换的消息类型，不写Message
     * 如果标在类上，可以用@RabbitHandler标记一个或多个方法来处理
     * 可以在多个方法上标记@RabbitHandler，每个方法的第二个参数类型不同，就能分别处理不同类型的消息
     */
/*    @RabbitListener(queues = {"queue.c"})
    public void testRabbitListener(Object message,
                                   OrderReturnReasonEntity returnReasonEntity, Channel channel) throws InterruptedException {
//        System.out.println("收到消息："+message);
//        System.out.println("消息类型："+message.getClass());
//        System.out.println("=============");
//        System.out.println(returnReasonEntity);
//        System.out.println("Channel(一个客户端跟Rabbit服务器只会建立一条长连接的通道)："+channel);
        Thread.sleep(1000);
        System.out.println(returnReasonEntity.getId());
    }*/

    /**
     * 如果不设置listener.simple.acknowledge-mode，则默认为auto，表示自动签收，队列收到确认后会删除消息
     * 但这个实际上是批量确认的，也就是收到一半如果中途宕机了，剩下的也会被确认，造成消息丢失
     * 另外要注意，debug模式下点击idea的停止按钮，进程还是会走完再关，要真正模拟宕机得用
     * cmd的netstat -ano|findStr {端口} 结合 taskkill /PID {上个步骤查到的PID} /F命令
     * 但尴尬的是发现如果用这种方式关，剩下的消息还在队列中（ready状态），并没有丢失，所以这个消息丢失
     * 很可能就是因为idea的debug模式点停止还是会跑完进程
     * <p>
     * 手动确认listener.simple.acknowledge-mode=manual
     */
    @RabbitHandler
    public void testRabbitListener(Message message, OrderReturnReasonEntity returnReasonEntity, Channel channel) throws InterruptedException {
        System.out.println(returnReasonEntity);
        System.out.println(message.getMessageProperties().getDeliveryTag());
        try {
            /*
            * Acknowledge one or several received messages. Supply the deliveryTag from the AMQP.Basic.GetOk or AMQP.Basic.Deliver method containing the received message being acknowledged.
            * Params:
            *
            * （1）deliveryTag – the tag from the received AMQP.Basic.GetOk or AMQP.Basic.Deliver
            *
            * （2）multiple –
            * true to acknowledge all messages up to and including the supplied delivery tag;
            * false to acknowledge just the supplied delivery tag.
            * 也就是说，如果是true，则所有小于等于传入deliveryTag的消息都会被确认
            *
            * */
            long deliveryTag = message.getMessageProperties().getDeliveryTag();
            if (deliveryTag %2 == 0){
                channel.basicAck(deliveryTag, false);
                System.out.println("ack"+message);
            }else {
                //拒收后会重新入队
                channel.basicNack(deliveryTag, false,true);
                System.out.println("nack"+message);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @RabbitHandler
    public void testRabbitListener2(Message message, String msg, Channel channel) throws InterruptedException {
        System.out.println(msg);
        System.out.println(message.getMessageProperties().getDeliveryTag());
        try {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void testSendMessage4() {
        for (long i = 0; i < 10; i++) {
            if (i % 2 == 0) {
                OrderReturnReasonEntity orderReturnReasonEntity = new OrderReturnReasonEntity();
                orderReturnReasonEntity.setId(i);
                orderReturnReasonEntity.setName("7天无理由");


                /**
                 * 这里还可以加第五个参数CorrelationData，相当于一个消息的id，可以在ReturnCallback方法中获取
                 * 怎么保证某个消息一定收到了？可以在ReturnCallback将收到的CorrelationData保存到数据库中，因为
                 * 调用ReturnCallback的都是投递消息失败的。然后每隔一段时间遍历重发，这样就保证了消息的可靠传输
                 * 当然，实际中不可能存到数据库中然后去遍历这样，这里只是提供一种思路
                 *
                 */
                rabbitTemplate.convertAndSend("exchange.topic", "c.c",
                        orderReturnReasonEntity, new CorrelationData(UUID.randomUUID().toString()));
            } else {
                rabbitTemplate.convertAndSend("exchange.topic", "c.c",
                        "hello world !!! " + i, new CorrelationData(UUID.randomUUID().toString()));
            }

        }
    }


}